/*=====================================================================

  File:      Surface.cs
  Summary:   A SQL Server user-defined aggregate that computes the surface of a set of triangles
  Date:	     May 27, 2006

---------------------------------------------------------------------

  This file is part of the Microsoft SQL Server Code Samples.
  Copyright (C) Microsoft Corporation.  All rights reserved.

This source code is intended only as a supplement to Microsoft
Development Tools and/or on-line documentation.  See these other
materials for detailed information regarding Microsoft code samples.

THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.

======================================================= */

using System;
using System.Data.Sql;
using System.Data.SqlTypes;
using System.Runtime.InteropServices;
using Microsoft.SqlServer.Server;

namespace Microsoft.Samples.SqlServer.DB3D
{
    // This is a SQL Server user-defined aggregate that allows the consumer
    // to compute the outer surface of a 3-dimentional object made of 
    // triangles using a set-based query.

    // Note that not only this is a custom aggregate, but also it's one
    // that aggregates a custom type (the Triangle user-defined type).

    // The surface is computed by adding up the surface of each invidual
    // triangle that's input to the UDT. The surface for each invidual
    // triangle is computed using Heron's formula that works for arbitrary
    // triangles.

	[Serializable]
	[SqlUserDefinedAggregate(Format.Native)]
	[StructLayout(LayoutKind.Sequential)]
	public class Surface
	{
		double _area;

		public void Init()
		{
			_area = 0;
		}

		public void Accumulate(Triangle value)
		{
			// Heron's Formula 
			double a = Math.Sqrt(Math.Pow(value.P1.X - value.P2.X, 2) +
				                 Math.Pow(value.P1.Y - value.P2.Y, 2) +
				                 Math.Pow(value.P1.Z - value.P2.Z, 2));
			double b = Math.Sqrt(Math.Pow(value.P2.X - value.P3.X, 2) +
					             Math.Pow(value.P2.Y - value.P3.Y, 2) +
					             Math.Pow(value.P2.Z - value.P3.Z, 2));
			double c = Math.Sqrt(Math.Pow(value.P3.X - value.P1.X, 2) +
					             Math.Pow(value.P3.Y - value.P1.Y, 2) +
					             Math.Pow(value.P3.Z - value.P1.Z, 2));
			double s = (a + b + c) / 2;

			_area += Math.Sqrt(s * (s - a) * (s - b) * (s - c));
		}

		public void Merge(Surface group)
		{
			_area += group._area;
		}

		public SqlDouble Terminate()
		{
			return new SqlDouble(_area);
		}
	}
}